#include "EventLoop.hpp"
#include "Acceptor.hpp"
#include "TcpConnection.hpp"

#include <sys/eventfd.h>
#include <unistd.h>
#include <stdio.h>
#include <mutex>

#define MAX_TIMEOUT 5000

namespace wd
{
using std::unique_lock;

EventLoop::EventLoop(Acceptor & acceptor)
    :_epfd(creatEpollFd())
     ,_accept(acceptor)
     ,_eventfd(creatEventFd())
     ,_evsArr(1000)
     ,_connPtr()
     ,_isLooping(false)
     ,_mutex()
    {
        addEpollEvent(_accept.getFd());
        addEpollEvent(_eventfd);
        _functor.reserve(10);
    }

EventLoop::~EventLoop(){
    close(_epfd);
}

void EventLoop::loop(){
    _isLooping = true;
    while(_isLooping){
        waitEpollFd();
    }
}

void EventLoop::closeLoop(){
    _isLooping = false;
}

void EventLoop::functorToLoop(Functor&& func){
    if(!func){
        return;
    }

    unique_lock<mutex> ul(_mutex);
    _functor.emplace_back(func);

    wakeup();
}

void EventLoop::doPendingFunctor(){
    vector<Functor> temp;
    {
        unique_lock<mutex> ul(_mutex);
        temp.swap(_functor);
    }
    for(auto &it: temp){
        it();
    }
}

void EventLoop::setCallBack(const CallBack& cb1,
                            const CallBack& cb2,
                            const CallBack& cb3)
{
    _onConnection = cb1;
    _onMessage = cb2;
    _onClosed = cb3;
}

int EventLoop::creatEpollFd(){
    int epfd = epoll_create1(0);
    if(epfd <0){
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }
    return epfd;
}

int EventLoop::creatEventFd(){
   int ret = eventfd(0,0); 
   if(ret <0){
       perror("creatEventFd");
   }
   return ret;
}

void EventLoop::waitEpollFd(){
    int nready = epoll_wait(_epfd,(struct epoll_event*)_evsArr.data()
                            ,_evsArr.size(),MAX_TIMEOUT);
    if(nready ==-1 && errno == EINTR){
        return;
    }
    if(nready ==-1){
        perror("epoll_wait");
        exit(EXIT_FAILURE);
    }
    if(nready ==0){
        printf("timeout\n");
        return;
    }

    int listenFd = _accept.getFd();
    for(auto &it :_evsArr){
        if(it.data.fd == listenFd){
            handleNewConnection();
        }else if(it.data.fd == _eventfd){
            handleReadEvent();
            doPendingFunctor();
        }
        else{
            handleMessage(it.data.fd);
        }
    }
}

void EventLoop::wakeup(){
    uint64_t one = 1;
    int ret = write(_eventfd,&one,sizeof(one));
    if(ret <0){
        perror("write");
    }
}

void EventLoop::addEpollEvent(int netfd){
    struct ::epoll_event ev; 
    ev.data.fd = netfd;
    ev.events = EPOLLIN;
    int ret = epoll_ctl(_epfd,EPOLL_CTL_ADD,netfd,&ev);
    if(ret <0){
        perror("epoll_ctl");
        return;
    }
}

void EventLoop::delEpollEvent(int netfd){
    struct ::epoll_event ev; 
    ev.data.fd = netfd;
    int ret = epoll_ctl(_epfd,EPOLL_CTL_DEL,netfd,&ev);
    if(ret <0){
        perror("epoll_ctl");
        return;
    }
}

void EventLoop::handleNewConnection(){
    int newFd = _accept.accept();
    if(newFd <0){
        return;
    }
    addEpollEvent(newFd);

    TcpConnectionPtr newcon (new TcpConnection(newFd));
    newcon->setCallBack(_onConnection,
                        _onMessage,
                        _onClosed);

    _connPtr.insert(std::make_pair(newFd,newcon));
    newcon->handleNewConnectionCallBack();
}

void EventLoop::handleMessage(int netfd){
    auto it =_connPtr.find(netfd);
    if(it!=_connPtr.end()){
        if(it->second->isClosed()){
            it->second->handleOnClosedCallBack();
            delEpollEvent(netfd);
            _connPtr.erase(netfd);
        }else{
            it->second->handleMessageCallBack();
        }
    }
} 

void EventLoop::handleReadEvent(){
    uint64_t howmany=0;
    int ret = read(_eventfd,&howmany,sizeof(howmany));
    if(ret <0){
        perror("handleReadEvent()--->read");
    }
    printf("howmany--->%ld \n",howmany);
}

}//end of namespace

